package com.xc.sys.service;

import com.google.common.base.Optional;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.xc.common.object.PageInfo;
import com.xc.common.tool.BeanAid;
import com.xc.db.Tables;
import com.xc.db.tables.SysOrganization;
import com.xc.db.tables.records.SysOrganizationRecord;
import com.xc.sys.entity.SysOrganizationEntity;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.ExecutionException;
import org.apache.commons.lang3.StringUtils;
import org.jooq.Condition;
import org.jooq.DSLContext;
import org.jooq.Record;
import org.jooq.Result;
import org.jooq.SelectField;
import org.jooq.SelectForUpdateStep;
import org.jooq.SelectSeekStepN;
import org.jooq.SortField;
import org.jooq.TableField;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class SysOrganizationService {
	@Autowired
	private DSLContext dsl;
	private SysOrganization ORG = SysOrganization.SYS_ORGANIZATION.as("ORG");

	public Record getById(Integer id) {
		Record record = this.dsl.select(new SelectField[0]).from(this.ORG).where(new Condition[] { ORG.ID.eq(id) })
				.fetchOne();
		return record;
	}

	public SysOrganizationEntity getEntityById(Integer id) {
		Record record = this.dsl.select(new SelectField[0]).from(this.ORG).where(new Condition[] { this.ORG.ID.eq(id) })
				.fetchOne();
		SysOrganizationEntity org = null;
		if (record != null) {
			org = (SysOrganizationEntity) record.into(SysOrganizationEntity.class);
		}
		return org;
	}

	@SuppressWarnings("unchecked")
	public PageInfo<SysOrganizationEntity> select(Condition cond, PageInfo<SysOrganizationEntity> pageInfo,
			TableField<SysOrganizationRecord, ?>... orderby) {
		Result<Record> result = null;
		if (cond == null) {
			result = dsl.select(new SelectField[0]).from(ORG).orderBy(orderby)
					.limit(pageInfo.getLimit().intValue()).offset(pageInfo.getBeginNum()).fetch();
		} else {
			SelectForUpdateStep<Record> su = dsl.select(new SelectField[0]).from(ORG).where(new Condition[] { cond }).orderBy(orderby)
					.limit(pageInfo.getLimit().intValue()).offset(pageInfo.getBeginNum());
			result = su.fetch();
		}
		pageInfo.setResult(result);
		pageInfo.setList(result.into(SysOrganizationEntity.class));
		return pageInfo;
	}

	public List<SysOrganizationEntity> select(Condition cond, SortField<?>... orderby) {
		Result<Record> result = null;
		if (cond == null) {
			result = dsl.select(new SelectField[0]).from(ORG).orderBy(orderby).fetch();
		} else {
			SelectSeekStepN<Record> su = dsl.select(new SelectField[0]).from(ORG).where(cond).orderBy(orderby);
			result = su.fetch();
		}
		List<SysOrganizationEntity> list = result.into(SysOrganizationEntity.class);
		return list;
	}

	public Long count(Condition cond) {
		Long count = null;
		count = dsl.selectCount().from(ORG).where(new Condition[] { cond }).fetchOneInto(Long.class);
		return count;
	}

	public void update(SysOrganizationEntity entity) {
		genCode(entity);
		
		if (entity.getParentId()==null||entity.getParentId().equals("")) {
			entity.setParentId(null);
			entity.setTier(Integer.valueOf(1));
		} else {
			Record pRecord = getById(Integer.valueOf(entity.getParentId()));
			if(pRecord!=null) {
				Integer tier = pRecord.get(this.ORG.TIER);
				if(tier!=null)
					entity.setTier(tier + 1);
			}
		}
		if(StringUtils.isEmpty(entity.getParentId())) {
			entity.setOrgId(entity.getId());
		}else {
			SysOrganizationEntity parentEntity = getFromCaches(Integer.valueOf(entity.getParentId()));
			if (parentEntity!=null) {
				entity.setOrgId(parentEntity.getOrgId());
			}
		}
		SysOrganizationRecord record = new SysOrganizationRecord();
		record.from(entity);
		invalidateOrgCache(entity.getId());
		orgNameCache.invalidate(entity.getName());
		orgNameCache.invalidate(entity.getShortName());
		dsl.executeUpdate(record);
	}

	public void insert(SysOrganizationEntity entity) {
		genCode(entity);
		if (StringUtils.isEmpty(entity.getParentId())) {
			entity.setParentId(null);
			entity.setTier(1);
		} else {
			Record pRecord = getById(Integer.valueOf(entity.getParentId()));
			entity.setTier(pRecord.get(ORG.TIER) + 1);
		}
		if(StringUtils.isEmpty(entity.getParentId())) {
			entity.setOrgId(entity.getId());
		}else {
			SysOrganizationEntity parentEntity = getFromCaches(Integer.valueOf(entity.getParentId()));
			entity.setOrgId(parentEntity.getOrgId());
		}
		SysOrganizationRecord record = new SysOrganizationRecord();
		record.from(entity);
		int id = dsl.insertInto(Tables.SYS_ORGANIZATION).set(record).returning(ORG.ID).fetchOne().getId();
		entity.setId(id + "");
		invalidateOrgCache(entity.getId());
		orgNameCache.invalidate(entity.getName());
		orgNameCache.invalidate(entity.getShortName());
	}

	private void genCode(SysOrganizationEntity entity) {
		int c = 0;
		String code = "";
		if(entity.getCode()==null || entity.getCode().equals("")) {
			if(entity.getParentId()==null || entity.getParentId().equals("")) {
				while(true) {
					c++;
					code = c+"";
					Long count = count(ORG.CODE.eq(code));
					if(count==0) {
						break;
					}
				}
			}else {
				SysOrganizationEntity parentEntity = getEntityById(Integer.valueOf(entity.getParentId()));
				while(true) {
					c++;
					code = parentEntity.getCode() + "." +  c;
					Long count = count(ORG.CODE.eq(code));
					if(count==0) {
						break;
					}
				}
			}
			entity.setCode(code);
			entity.setOrgCode(code.replace(".", ""));
		}
	}

	public void deleteById(Integer id) {
		SysOrganizationEntity entity = getFromCaches(id);
		invalidateOrgCache(id);
		if(entity!=null) {
			orgNameCache.invalidate(entity.getName());
			orgNameCache.invalidate(entity.getShortName());
		}
		dsl.delete(this.ORG).where(new Condition[] { this.ORG.ID.eq(id) }).execute();
	}

	
	public static String getDetailOrgName(String orgId){
		if(StringUtils.isEmpty(orgId)) {
			return "";
		}
		SysOrganizationEntity nodeOrg = SysOrganizationService.getFromCaches(orgId);
		if(nodeOrg==null) {
			return "";
		}
		if(nodeOrg.getParentId()==null) {
			return nodeOrg.getShortName();
		}
		Collection<SysOrganizationEntity> orgs = SysOrganizationService.getOrgWithAncestors(Integer.valueOf(orgId));
		String orgName = "";
		for(SysOrganizationEntity org : orgs) {
			if(orgName.length()>0) {
				orgName += " / ";
			}
			orgName += org.getShortName();
		}
		return orgName;
	}
	public static Collection<SysOrganizationEntity> getOrgWithAncestors(Integer orgId){
		Stack<SysOrganizationEntity> stack = new Stack<>();
		addParentOrg(stack,orgId);
		List<SysOrganizationEntity> list = new ArrayList<>();
		while(!stack.isEmpty()) {
			SysOrganizationEntity o = stack.pop();
			list.add(o);
		}
		return list;
	}
	/**
	 * 
	 * 作者：曾平
	 * 日期：2020年4月29日 下午1:31:48 
	 * 描述：获取父机构，不包含顶部节点
	 */
	private static void addParentOrg(Collection<SysOrganizationEntity> stack ,Integer orgId){
		if (orgId==null) {
			return ;
		}
		SysOrganizationEntity mystore = SysOrganizationService.getFromCaches(orgId);
		if(mystore!=null) {
			if (StringUtils.isNotEmpty(mystore.getParentId())) {
				stack.add(mystore);
				addParentOrg(stack,Integer.valueOf(mystore.getParentId()));
			}
		}
	}
	/*
	 * 
	 * 功能：根据机构ID查询公司
	 * 作者：曾平
	 * 时间：2019年11月30日 下午5:07:41
	 */
	public SysOrganizationEntity getTopOrg(Integer storeId) {
		SysOrganizationEntity mystore = getEntityById(storeId);
		if(mystore==null) {
			return null;
		}
		String[] codes = StringUtils.split(mystore.getCode(),".");
		List<SysOrganizationEntity> orgs = select(ORG.CODE.eq(codes[0]));
		if (orgs.size() == 0) {
			return null;
		}
		return orgs.get(0);
	}
	public List<SysOrganizationEntity> getAllSubOrgs(Integer orgId) {
		SysOrganizationEntity org = getEntityById(orgId);
		Condition condOrg = ORG.CODE.like(org.getCode() + "%");
		List<SysOrganizationEntity> orgs = select(condOrg);
		return orgs;
	}
	public static LoadingCache<Integer, Optional<SysOrganizationEntity>> orgCache = CacheBuilder.newBuilder()
			.maximumSize(5000).build(new CacheLoader<Integer, Optional<SysOrganizationEntity>>() {
				public Optional<SysOrganizationEntity> load(Integer id) {
					SysOrganizationService service = BeanAid.getBean(SysOrganizationService.class);
					SysOrganizationEntity user = service.getEntityById(id);
					return Optional.fromNullable(user);
				}
			});

	public static SysOrganizationEntity getFromCaches(Integer key) {
		if (key == null) {
			return null;
		}
		try {
			return orgCache.get(key).orNull();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		return null;
	}
	public static SysOrganizationEntity getFromCaches(String key) {
		if (key == null) {
			return null;
		}
		try {
			return orgCache.get(Integer.valueOf(key)).orNull();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		return null;
	}
	public static LoadingCache<String, Optional<SysOrganizationEntity>> orgNameCache = CacheBuilder.newBuilder()
			.maximumSize(5000).build(new CacheLoader<String, Optional<SysOrganizationEntity>>() {
				public Optional<SysOrganizationEntity> load(String name) {
					SysOrganizationService service = BeanAid.getBean(SysOrganizationService.class);
					SysOrganization ORG = SysOrganization.SYS_ORGANIZATION.as("ORG");
					List<SysOrganizationEntity> list = service.select(DSL.or(ORG.NAME.eq(name),ORG.SHORT_NAME.eq(name)), ORG.CODE.asc());
					if(list.size()==0){
						return Optional.absent();
					}
					return Optional.fromNullable(list.get(0));
				}
			});
	
	public static SysOrganizationEntity getFromNameCaches(String key) {
		if (key == null) {
			return null;
		}
		Optional<SysOrganizationEntity> ca = null;
		try {
			ca = orgNameCache.get(key);
		} catch (ExecutionException e1) {
			e1.printStackTrace();
		}
		if(ca==null){
			return null;
		}
		return ca.orNull();
	}
	private void invalidateOrgCache(Integer id) {
		orgCache.invalidate(id);
	}
	private void invalidateOrgCache(String id) {
		orgCache.invalidate(Integer.valueOf(id));
	}
}
